package com.zhz.common.feign;

import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSON;
import com.zhz.common.tool.mybatisplus.base.core.contract.R;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;

import java.util.Objects;



/**
 * GlobalFeignFallback
 * <p>
 * 为 feign 提供全局默认的 fallback 处理，将 feign 调用失败的异常信息封装成 {@link R} 返回，而不是直接向上抛出异常，
 * 用户获取到 result 结果之后，应该判断 result 是否成功，而不是直接获取 data
 *
 * @author zhouhengzhe
 *
 */
@Slf4j
@Aspect
public class GlobalFeignFallback implements Ordered {

    @Pointcut("@within(org.springframework.cloud.openfeign.FeignClient)")
    public void feignClient(){}


    @Pointcut("execution(com.zhz.common.tool.mybatisplus.base.core.contract.R *.*(..))")
    public void resultReturnType(){}



    @Around("feignClient() && resultReturnType()")
    public R doFallback(ProceedingJoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        long end;
        try {
            R result = (R) joinPoint.proceed();
            if (Objects.nonNull(result) && result.isFail()) {
                end = System.currentTimeMillis();
                log.warn("feign 接口[{}]执行状态异常: {}, 接口参数: {}, 请求耗时: {}",
                        getMethodName(joinPoint), result, getParams(joinPoint), (end - start));
            }
            return result;
        } catch (Throwable cause) {
            end = System.currentTimeMillis();
            cause = unwrapSuperCause(cause, RuntimeException.class);
            log.error("feign 接口[{}]调用失败: {}, 接口参数: {}, 请求耗时: {}",
                    getMethodName(joinPoint), cause.getMessage(), getParams(joinPoint), (end - start), cause);
            return new FeignFallbackResult(cause.getMessage(), cause, start, end);
        }
    }


    /**
     * 解析方法名称
     */
    private String getMethodName(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        return ClassUtils.getQualifiedMethodName(methodSignature.getMethod());
    }


    /**
     * 解析方法参数
     */
    private String getParams(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        String params = ArrayUtil.isNotEmpty(args) ? JSON.toJSONString(args) : "[]";
        if (params.length() > 1000)  {
            params = params.substring(0, 1000) + "...";
        }
        return params;
    }


    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 10;
    }




    /**
     * 当目标异常为指定异常类型，且父异常不为空时，返回父异常，否则返回原异常
     */
    private static Throwable unwrapSuperCause(Throwable cause, Class<? extends Throwable> targetType) {
        if (targetType.isInstance(cause) && Objects.nonNull(cause.getCause())) {
            return cause.getCause();
        }
        return cause;
    }

}
